﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Engine
{
    /// <summary>This class contain information about point in lattice.</summary>
    [Serializable]
    public class Point
    {
        #region Fields

        /// <summary>position of point in x dimension.</summary>
        private int _x;
        
        /// <summary>position of point in y dimension.</summary>
        private int _y;

        /// <summary>position of point in z dimension.</summary>
        private int _z;

        /// <summary>Get list to all _bonds that connected to this point.</summary>
        private readonly List<Bond> _bonds;

        /// <summary>Indicate if we have tralled this point when check on conduct this lattice.</summary>
        private bool _isChecked;

        ///<summary>Indicate that we reached the other end of lattice.</summary>
        private bool _isFinalPoint;
        
        ///<summary>Used in calculating of conductivity. Index of this point in equation.</summary>
        private int? _index = null;
        
        ///<summary>Potential of this node.</summary>
        private double? _potential;

        #endregion

        #region Constructor

        /** Standard constructor 
         * for 2 dimensional lattice. */
        public Point(int x, int y)
        {
            this._x = x;
            this._y = y;
            this._z = -1;
            _bonds = new List<Bond>();
            _isChecked = false;
            _isFinalPoint = false;
        }

        /** As previous, but we can specify that this 
         * point is other end of lattice. */
        public Point(int x, int y, bool isFinal) : this(x, y)
        {
            _isFinalPoint = isFinal;
        }

        public Point(int x, int y, int z)
            : this(x, y)
        {
            this._z = z;
        }
        #endregion

        #region Properties

        /**<summary>Number of points in x directions.</summary> */
        public int X
        {
            get { return _x; }
            set { _x = value; }
        }
        
        /**<summary>Bumber of points in y directions.</summary> */
        public int Y
        {
            get { return _y; }
            set { _y = value; }
        }

        /**<summary>Number of points in z directions.</summary> */
        public int Z
        {
            get { return _z; }
            set { _z = value; }
        }

        /** <summary>Indicate if we have tralled this point when check on conduct this lattice.</summary> */
        public bool IsChecked
        {
            get { return _isChecked; }
            set { _isChecked = value; }
        }

        /**<summary>Indicate that we reach other end of lattice.</summary>*/
        public bool IsFinalPoint
        {
            get { return _isFinalPoint; }
            set { this._isFinalPoint = value; }
        }

        /** Gets read only list of all _bonds that connected 
         * to this point. */
        public IList<Bond> BondList
        {
            get { return _bonds.AsReadOnly(); }
        }

        /**<summary>Index of this point in SLAY.</summary>*/
        public int? Index
        {
            get { return _index;  }
            set { _index = value; }
        }

        /**<summary>Potential of this node.</summary>*/
        public double? Potential 
        {
            get { return _potential; }
            set { _potential = value; }
        }

        #endregion

        #region Methods

        /**Called within bond instance when we set Start or End point.*/
        internal void AddBond(Bond item)
        {
            _bonds.Add(item);
        }

        internal void RemoveBond(Bond item)
        {
            _bonds.Remove(item);
        }
        
        /** Remove any relationships 
         * to this point. */
        public void RemovePoint()
        {
            foreach(Bond bond in _bonds)
            {
                bond.RemovePoint(this);
            }
            _bonds.Clear();
        }

        /** Get the other end of this bond. */
        public Point Navigate(Bond bond)
        {
            if(this == bond.StartPoint)
            {
                return bond.EndPoint;
            }
            else if (this == bond.EndPoint)
            {
                return bond.StartPoint;
            }
            else
            {
                throw new ArgumentException("This bond have not any relationships with this point.");
            }
        }

        /// <summary>
        /// Check if we have any working bond with specified point
        /// </summary>
        /// <param name="point">point with which we try to find bond</param>
        /// <returns></returns>
        public bool IsConnectedWithPoint(Point point)
        {
            bool result = false;
            foreach (var bond in _bonds )
            {
                Point connected = this.Navigate(bond);
                if(Point.ValueEquals(point,connected))
                {
                    result = true;
                }
            }
            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        public Bond GetConnection(Point point)
        {
            Bond result = null;
            foreach (var bond in _bonds)
            {
                Point connected = this.Navigate(bond);
                if (Point.ValueEquals(point, connected))
                {
                    result = bond;
                }
            }
            return result;
        }


        /// <summary>
        /// Compare with
        /// </summary>
        /// <param param name="point1">seriesPercolationThreashold point to compare</param>
        /// <param name="point2">second point to compare</param>
        /// <returns>Is two points equals by value</returns>
        public static bool ValueEquals(Point point1, Point point2)
        {
            //todo modify code to support 3d dimension
            if(point1._x == point2._x && point1._y == point2._y)
            {
                return true;
            }
            else
            {
                return false;
            }
        }


        /**<summary>For visualization purposes.</summary>*/
        public override string ToString()
        {
            if(_z == -1)
            {
                return "[Point _x="+ _x +"; _y=" + _y + ";]";
            }
            else
            {
                return "[Point _x=" + _x + "; _y=" + _y + "; _z="+ _z +";]";
            }
        }

        #endregion
    }
}
